package com.tao.net

import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.elvishew.xlog.XLog
import com.tao.data.Resource
import com.tao.di.getError
import com.tao.di.getNetwork
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.onCompletion
import kotlinx.coroutines.launch
import org.json.JSONException
import retrofit2.HttpException
import java.net.ConnectException
import java.net.SocketTimeoutException
import java.util.concurrent.TimeoutException

/**
 * ViewModel扩展
 */
fun <T> ViewModel.request(
    flow: Flow<T>,
    liveData: MutableLiveData<Resource<T>>,
    witch: Int = 0
) {
    viewModelScope.request(flow, liveData, witch)
}

/**
 * 发送请求，并处理flow的返回结果
 */
fun <T> CoroutineScope.request(
    flow: Flow<T>,
    liveData: MutableLiveData<Resource<T>>,
    witch: Int = 0
) {
    //请求之前操作
    val network = getNetwork()
    if (!network.isConnected()) {
        // 无网络
        val errors = getError()
        liveData.value = Resource.Error(witch, errors.getError(ERROR_NO_INTERNET))
        return
    }
    liveData.value = Resource.Start(witch)
    this.launch {
        //网络请求
        flow.catch { // 异常捕获处理
            XLog.e("Exception", it)
            val errors = getError()
            val error = when (it) {
                // 连接超时
                is ConnectException,
                    // Socket网络异常
                is SocketTimeoutException,
                    // 数据解析异常
                is JSONException,
                    // 请求超时
                is TimeoutException -> errors.getError(ERROR_REQUEST)
                is HttpException -> {
                    // non-2xx HTTP response
                    val he = it as HttpException
                    errors.getError(he.code(), he.message())
                }
                is ServerException -> {
                    // todo 处理服务端自定义code
                    val se = it as ServerException
                    errors.getError(se.code(), se.message())
                }
                else -> {
//                    it.message ?: errors.getError(ERROR_UNKNOWN)
                    it.message?.let { cause ->
                        // msg不为空
                        errors.getError(ERROR_UNKNOWN, cause)
                    } ?: errors.getError(ERROR_UNKNOWN) // msg为空
                }
            }
            liveData.value = Resource.Error(witch, error)
        }
            .onCompletion {
                // 结束
                liveData.value = Resource.Complete(witch)
            }
            //数据请求返回处理
            .collect {
                liveData.value = Resource.Success(witch, it)
            }
    }
}

fun <T> ViewModel.request(
    block: suspend CoroutineScope.() -> T,
    onStart: (() -> Unit)? = null,
    onSuccess: ((T) -> Unit)? = null,
    onError: ((Pair<Int, String>) -> Unit)? = null,
    onComplete: (() -> Unit)? = null,
) {
    // 开始
    onStart?.invoke()
    viewModelScope.launch(CoroutineExceptionHandler { _, throwable ->
        XLog.e("Exception", throwable)
        val errors = getError()
        val error = when (throwable) {
            // 连接超时
            is ConnectException,
                // Socket网络异常
            is SocketTimeoutException,
                // 数据解析异常
            is JSONException,
                // 请求超时
            is TimeoutException -> errors.getError(ERROR_REQUEST)
            is HttpException -> {
                // non-2xx HTTP response
                val he = throwable as HttpException
                errors.getError(he.code(), he.message())
            }
            is ServerException -> {
                // todo 处理服务端自定义code
                val se = throwable as ServerException
                errors.getError(se.code(), se.message())
            }
            else -> {
//                    it.message ?: errors.getError(ERROR_UNKNOWN)
                throwable.message?.let { cause ->
                    // msg不为空
                    errors.getError(ERROR_UNKNOWN, cause)
                } ?: errors.getError(ERROR_UNKNOWN) // msg为空
            }
        }
        onError?.invoke(error)
    }) {
        val ret = block.invoke(this)
        onSuccess?.invoke(ret)
        onComplete?.invoke()
    }
}
